<!--
  通用表单 孙小寒 2022-4-5
-->
<script>
import {computed, ref, toRefs} from 'vue'
import validate, {fieldValid} from '/src/utils/validate'
import {View, Button} from '@tarojs/components'

export default {
  components: {},
  props: {
    modelValue: {
      type: Object,
      required: true
    },
    fields: {
      type: Array,
      required: true
    },
    labelWidth: {
      type: String,
      default: () => ''
    },
    inputAlign: {
      type: String,
      default: () => ''
    },
    disabled: {
      type: Boolean,
      default: false
    },
    readonly: {
      type: Boolean,
      default: false
    },
    isLabelLine: {
      type: Boolean,
    },
    // 加载中
    loading: {
      type: Boolean,
      default: false
    },
    // 提交时是否验证表单
    valid: {
      type: Boolean,
      default: true
    },
    // 单个字段验证时机 none不验证
    validTrigger: {
      type: String,
      default: () => '',
    },
    // 显示提交按钮
    submitButton: {
      type: Boolean,
      default: true
    },
    // 显示重置按钮
    resetButton: {
      type: Boolean,
      default: true
    },
    // 提交按钮的显示文字
    submitButtonText: {
      type: String,
      default: '提交'
    },
  },
  setup(props, context) {
    const {fields} = toRefs(props)
    const visibleFields = computed(() => {
      return fields.value.filter(i => !i.hidden)
    })
    const validFields = computed(() => {
      const arr = []
      visibleFields.value.forEach(i => {
        if (i instanceof Array) {
          arr.push(...i)
        } else {
          arr.push(i)
        }
      })
      arr.filter(i => !(i.hidden || i.type === 'separator'))
      return arr
    })
    const validObj = ref({})
    return {
      visibleFields,
      validFields,
      validObj
    }
  },
  render() {
    return (
      <View className="m-form-view">
        {this.buildFieldsView()}
        {this.buildButtonView()}
      </View>
    )
  },
  methods: {
    buildFieldsView() {
      return this.visibleFields.map((field, index) => {
        // 定制特定字段节点
        if (this.$slots[field.prop]) {
          return this.$slots[field.prop](field)
        }
        // 允许定制每个字段的节点
        if (this.$slots.$custom) {
          return this.$slots.$custom(field)
        }
        if (field.render) return field.render()
        // 分隔符
        if (field.type === 'separator') {
          return (
            <View class="separator-view">
              <View style={'background-color:' + this.$clr}/>
              {field.label}
            </View>
          )
        }
        if (field instanceof Array) {
          return this.buildMultipartField(field)
        }
        return this.buildSingleField(field)
      })
    },
    /**
     * 创建单行多个field
     * @param fields
     * @returns {JSX.Element}
     */
    buildMultipartField(fields) {
      const multipartField = fields.map(field => {
        if (field.type === 'separator') {
          return <View class="multipart-separator-view">
            {field.label}
          </View>
        }
        return this.buildSingleField(field)
      })
      return (
        <View class="multipart-field-view">
          {multipartField}
        </View>
      )
    },
    /**
     * 构建单行field
     */
    buildSingleField(field) {
      const rules = (field.rule instanceof Array) ? field.rule : [field.rule || {}]
      const required = rules.some(i => i.required) || field.required
      const maxlength = rules.find(i => i.maxLength) && rules.find(i => i.maxLength).maxLength || field.maxlength
      const options = rules.find(i => i.options) && rules.find(i => i.options).options || field.options
      const fieldParams = {
        key: field.prop,
        ...field,
        ...this.validObj[field.prop],
        required,
        maxlength,
        options,
        valid: field.hasOwnProperty('valid') ? field.valid : this.valid,
        validTrigger: field.validTrigger || this.validTrigger,
        disabled: field.hasOwnProperty('disabled') ? field.disabled : this.disabled,
        readonly: field.hasOwnProperty('readonly') ? field.readonly : this.readonly,
        isLabelLine: field.hasOwnProperty('isLabelLine') ? field.isLabelLine : this.isLabelLine,
        isLink: field.hasOwnProperty('isLink') ? field.isLink : ['select', 'date', 'datetime', 'year-month'].includes(field.type),
        labelWidth: field.labelWidth || this.labelWidth,
        inputAlign: field.inputAlign || this.inputAlign,
        placeholder: field.hasOwnProperty('placeholder') ? field.placeholder : (['select', 'date', 'time', 'datetime', 'year-month'].includes(field.type) ? '请选择' : '请输入') + field.label,
        // onChange: e => this.emitEvent('change', field, e),
      }
      // 插槽
      const slots = {
        ...field.slots,
      }
      return (
        <m-field
          {...fieldParams}
          v-model={this.modelValue[field.prop]}
          v-slots={slots}
          onChange={e => this.validField(fieldParams, 'change', e)}
          onBlur={e => this.validField(fieldParams, 'blur', e.detail.value)}
        />
      )
    },
    /**
     * 构建按钮
     */
    buildButtonView() {
      const btnArr = []
      if (this.resetButton) {
        btnArr.push((
          <Button className="reset-button-view" hover-class="reset-button-view--hover" onTap={this.reset}>
            重置
          </Button>
        ))
      }
      if (this.submitButton) {
        btnArr.push((
          <Button class="submit-button-view" hover-class="submit-button-view--hover" onTap={this.submit}>
            {this.submitButtonText}
          </Button>
        ))
      }
      return (
        <View class="button-view">
          {btnArr}
        </View>
      )
    },
    reset() {
      this.validObj = {}
      this.validFields.forEach(i => {
        delete this.modelValue[i.prop]
      })
      this.$emit('reset')
    },
    submit() {
      const validRule = this.validFields.reduce((a, field) => {
        a[field.prop] = this.getRules(field)
        return a
      }, {})
      this.$nextTick(async() => {
        const validResult = await validate(this.modelValue, validRule)
        this.validObj = {}
        if (validResult.validError) {
          validResult.errFields.forEach(errField => {
            this.validObj[errField.fieldName] = {
              error: !errField.result,
              errorMessage: errField.errMsg
            }
          })
        }
        this.$emit('submit', validResult)
      })
    },
    /**
     * 单个field验证
     * @param fieldParams
     * @param eventName
     * @param val
     */
    validField(fieldParams, eventName, val) {
      if (fieldParams.valid && eventName === fieldParams.validTrigger) {
        let rules = this.getRules(fieldParams)
        if (rules.length && rules[0]) {
          fieldValid(fieldParams.prop, rules, val, this.modelValue).then(res => {
            this.validObj[fieldParams.prop] = {
              error: !res.result,
              errorMessage: res.errMsg
            }
          })
        }
      }
    },
    getRules(fieldParams) {
      let rules = (fieldParams.rule instanceof Array) ? fieldParams.rule : [fieldParams.rule]
      if (rules.length && rules[0]) {
        rules.forEach(i => i.label || (i.label = fieldParams.label))
      }
      return rules
    }
  }
}
</script>
<style lang="scss">
.m-form-view {
  padding: 0 10px;
  background-color: white;

  .separator-view {
    font-size: 1.1em;
    font-weight: bold;
    margin-top: 0.5em;
    display: flex;
    align-items: center;

    > view {
      width: 0.3em;
      height: 1.1em;
      margin-right: 10px;
    }

  }

  .multipart-field-view {
    display: flex;
    align-items: stretch;

    .m-field-view {
      flex-grow: 1;
    }

    .multipart-separator-view {
      padding: 0.75em 0.5em 0 0.5em;
      border-bottom: 1rpx solid #f3f3f3;

      > view {
        align-items: center;
      }
    }
  }

  .button-view {
    display: flex;
    flex-direction: column;

    > button {
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 1.2em;
      margin: 0.25em 0;
      border-radius: 0.5em;
      border: 1rpx solid #f1f1f1;
    }

    button::after {
      border: none;
    }

    .reset-button-view {
      background-color: #e5e5e1;
      color: inherit;
    }

    .reset-button-view--hover {
      opacity: 0.8;
    }

    .submit-button-view {
      color: white;
      background-color: #ea7c4b;
    }

    .submit-button-view--hover {
      opacity: 0.8;
    }
  }
}
</style>
